﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Starfire3d.Data.Objects;
using Starfire3d.Managers;

namespace Starfire3d.Data.States.UnitStates
{
    public class AttackingUnit : BaseState<Unit>
    {
        private Unit currentTarget = null;

        public override void Enter(Unit entity)
        {
        }

        public override void Execute(Unit entity)
        {
            if (entity.IsDead)
            {
                entity.StateMachine.ChangeState(new DeadUnit());
                return;
            }

            if (DataStore.Store.Count <= 1)
            {
                entity.StateMachine.ChangeState(new IdleUnit());
                return;
            }

            if (currentTarget != null && currentTarget.IsDead)
                currentTarget = null;

            // Pick a random target to attack
            if (currentTarget == null)
            {
                currentTarget = entity.Targeting.PickTarget(entity);
                
                if (currentTarget == null)
                    return;
            }

            int range = entity.DistanceTo(currentTarget);

            if (range > entity.Targeting.SelectRange(entity) && MovementManager.CanMove(entity.Sheet.Details.MaxSpeed))
            {
                entity.Data.Add("PX", entity.Data.GetInt("X"), true);
                entity.Data.Add("PY", entity.Data.GetInt("Y"), true);

                if (currentTarget.Data.GetInt("X") > entity.Data.GetInt("X"))
                    entity.Data.Add("X", entity.Data.GetInt("X") + 1, true);
                if (currentTarget.Data.GetInt("X") < entity.Data.GetInt("X"))
                    entity.Data.Add("X", entity.Data.GetInt("X") - 1, true);
                if (currentTarget.Data.GetInt("Y") > entity.Data.GetInt("Y"))
                    entity.Data.Add("Y", entity.Data.GetInt("Y") + 1, true);
                if (currentTarget.Data.GetInt("Y") < entity.Data.GetInt("Y"))
                    entity.Data.Add("Y", entity.Data.GetInt("Y") - 1, true);
            }

            foreach (WeaponSystem weapon in entity.Sheet.Details.Weapons)
            {
                if (weapon.LastFired > 0)
                    weapon.LastFired -= 1;
                else
                {
                    int toHit = weapon.ToHit(range, entity) + weapon.HitModifiers(entity, currentTarget);
                    if (weapon.InRange(range) && toHit > 0)
                    {
                        Ordinance missile = new Ordinance(weapon);
                        missile.Entity_Name = Guid.NewGuid().ToString();
                        missile.Data.Add("X", entity.Data.GetInt("X"), true);
                        missile.Data.Add("Y", entity.Data.GetInt("Y"), true);
                        missile.Data.Add("DMG", weapon.Damage(range), true);
                        missile.Target = currentTarget.Entity_Name;

                        int rollToHit = RandomGenerator.Rand(10);
                        if (rollToHit > toHit)
                        {
                            missile.Data.Add("MISSED", true, true);
                            missile.Data.Add("TRAVEL", range / 3, true);
                        }

                        DataStore.Add(missile);

                        weapon.LastFired = 16 / weapon.ROF;
                    }
                }
            }
        }

        public override void Exit(Unit entity)
        {
        }

        public override string ToString()
        {
            return "Attacking";
        }
        public override string ToString(Unit entity)
        {
            if (currentTarget == null)
                return ToString();

            return "Atk : "
                + "(" + entity.Sheet.Details.UndamagedSystems().ToString() + "/" + entity.Sheet.Details.Systems.Count.ToString() + "/" + entity.Sheet.Details.Weapons.Count().ToString() + ") "
                + currentTarget.Entity_Name + ":" + entity.DistanceTo(currentTarget).ToString();
        }
    }
    /*
    var targets = 
     //30% chance to ignore damage enemy can do to them, and just go for highest-value targets 
     ( unit.UnitType.AIAlwaysStrikeStrongestAgainst || 
     AILoop.Instance.AIRandom.Next( 0, 100 ) < 30 ? 
     from obj in rollup.EnemyUnits 
     where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post 
      Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter, 
      obj.LocationCenter ) < Configuration.GUARD_RADIUS ) 
     orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority 
         obj.GetHasAttackPenaltyAgainstThis( unit ) ascending, //ships that we have penalties against are the last to be hit 
         (double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy out of its total health 
         obj.IsProtectedByForceField ascending, //avoid ships that are under force fields 
         obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies 
         Mat.ApproxDistanceBetweenPoints( obj.LocationCenter, unit.LocationCenter ) ascending, //how close am I to the enemy 
         obj.UnitType.ShieldRating ascending, //how high are their shields 
         unit.UnitType.AttackPower ascending, //go for the lowest-attack target (economic, probably) 
         obj.Health ascending //how much health the enemy has left 
     select obj 
   : 
       from obj in rollup.EnemyUnits 
       where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post 
          Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter, 
          obj.LocationCenter ) < Configuration.GUARD_RADIUS ) 
       orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority 
           ( chooseWeaklyDefendedTarget ? 
           obj.UnitType.TripleBasicFirePower >= obj.NearbyMilitaryUnitPower : 
           ( chooseStronglyDefendedTarget ? 
           obj.UnitType.TripleBasicFirePower < obj.NearbyMilitaryUnitPower : true ) ) descending, //lightly defended area 
           (double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy out of its total health 
           obj.IsProtectedByForceField ascending, //avoid ships that are under force fields 
           obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies 
           obj.GetHitPercent( unit ) descending, //how likely I am to hit the enemy 
           unit.GetAttackPowerAgainstThis( obj, false ) descending, //how much damage the enemy can do to me 
           obj.Health ascending //how much health the enemy has left 
       select obj 
   ); 

      
    bool foundTarget = false; 
    foreach ( AIUnit enemyUnit in targets ) 
    { 
        if ( enemyUnit.Health <= 0 || enemyUnit.CloakingLevel == CloakingLevel.Full ) 
            continue; //skip targets that are already dead, or are cloaked 
        if ( unit.CloakingLevel == CloakingLevel.Full &&  
            enemyUnit.UnitType.ShipType == ShipType.Scout ) 
            continue; //don't give away the position of cloaked ships to scouts 
        if ( unit.CloakingLevel != CloakingLevel.None && 
            enemyUnit.UnitType.TachyonBeamRange > 0 ) 
            continue; //cloaked ships will not attack tachyon beam sources 
        if ( enemyUnit.UnitType.VeryLowPriorityTarget ) 
            continue; //if it is a very low priority target, just skip it 
        if ( enemyUnit.IsProtectedByCounterMissiles && unit.UnitType.ShotIsMissile ) 
            continue; //if enemy is protected by counter-missiles and we fire missiles, skip it 
        if ( enemyUnit.IsProtectedByCounterSnipers && unit.UnitType.ShotIsSniper ) 
            continue; //if enemy is protected by counter-sniper flares and we fire sniper shots, skip it 
        if ( enemyUnit.GetAttackPowerAgainstThis( unit, false ) == 0 ) 
            continue; //if we are unable to hurt the enemy unit, skip attacking it 
        if ( unit.EffectiveMoveSpeed == 0 && !unit.UnitType.UsesTeleportation &&  
            enemyUnit.GetHitPercent( unit ) <> 
            continue; //stop ourselves from targeting fixed ships onto long-range targets 

        gc = GameCommand.Create( GameCommandType.SetAttackTarget, true ); 
        gc.FGObjectNumber1 = unit.FGObjectNumber; 
        gc.FGObjectNumber2 = enemyUnit.FGObjectNumber; 
        gc.Integer1 = 0; //Not Attack-Moving 
        unit.LastAICommand = gc.AICommand; 
        AILoop.Instance.RequestCommand( gc ); 
        foundTarget = true; 
        break; 
    } 

    //if no target in range, and guarding, go back to base if out of range 
    if ( !foundTarget && unit.GuardingObjectNumber > 0 ) 
    { 
        Point guardCenter = unit.GuardingObject.LocationCenter; 
        if ( Mat.ApproxDistanceBetweenPoints( guardCenter, unit.LocationCenter ) > 
            Configuration.GUARD_RADIUS ) 
            Move( unit, guardCenter ); 
    } 
     */
}
